(function(root) {


    var optionCache = {};

    /**
     *  jQuery 特点 
     * 
     * 1. 无new 构建实例对象
     * 
     * 2. 共享原型
     * 
     * 3. extend 拓展
     * 
     */

    function jQuery() {
        /**
         *  形成死循环  
         */
        // return new jQuery()  
        return new jQuery.prototype.init()
    }
    /**
     *  重写jQuery 原型，  来实现 构建无new操作符实例 ， 将jQuery的方法放在原型上
     */
    jQuery.prototype = {
            /**
             *  构建实例对象
             */
            init: function() {

            },
            html: function() {

            },
            css: function() {

                }
                //  ...
        }
        /**
         *  简写原型 
         */
    jQuery.fn = jQuery.prototype
        /**
         *  实现原型 共享 原型
         */
    jQuery.fn.init.prototype = jQuery.fn;


    /**
     *  extend 
     * 
     * 1. 可以给任意多个对象合并
     * 
     * 2. 可以拓展jQuery本身
     * 
     * 3. 可以拓展jQuery实例方法
     */

    jQuery.extend = jQuery.fn.extend = function() {
        /**
         * 参数分为多种情况 
         * 
         * 1. 有多个参数时 
         *      1. 第一个参数为 boolean 且为true 时  则代表要进行深拷贝
         *      
         *      2. 否则第一个参数必为对象
         * 
         * 2. 只有一个参数时
         *      
         *      1. 代表着给jQuery本身 / jQuery 实例 添加拓展方法
         * 
         */
        var target = arguments[0];
        var length = arguments.length;
        var deep = false
        var i = 1;

        if (Object.prototype.toString.call(target).slice(8, -1) === 'Array') {
            deep = true;
            target = arguments[1]
            i++
        }

        if (typeof target != 'object') {
            target = {}
        }

        if (i === length) {
            target = this;
            i--
        }



        for (; i < length; i++) {
            var option = arguments[i];
            for (var name in option) {
                var copy = option[name];
                var src = target[name];
                var isArray = Object.prototype.toString.call(copy).slice(8, -1) === 'Array';
                var isObject = Object.prototype.toString.call(copy).slice(8, -1) === 'Object'
                var clone = {};
                if (deep && (isArray || isObject)) {
                    if (isArray) {
                        clone = Object.prototype.toString.call(src).slice(8, -1) === 'Array' ? src : [];
                    } else if (isObject) {
                        clone = Object.prototype.toString.call(src).slice(8, -1) === 'Object' ? src : {}
                    } else {
                        clone = src
                    }
                    target[name] = jQuery.extend(deep, clone, copy)
                } else if (copy !== undefined) {
                    target[name] = copy
                }
            }
        }

        return target


    }


    jQuery.extend({
        isPlainObject: function(data) {
            return Object.prototype.toString.call(data).slice(8, -1) === 'Object'
        },
        isFunction: function(fn) {
            return Object.prototype.toString.call(fn).slice(8, -1) === 'Function'
        },
        isArray: function(data) {
            return Object.prototype.toString.call(data).slice(8, -1) === 'Array'
        },
        isBoolean: function(data) {
            return Object.prototype.toString.call(data).slice(8, -1) === 'Boolean'
        },

        callbacks: function(option) {
            //  option 判断option 是否为字符串 若是字符串，先查看缓存内有没，有就用缓存 ,没有就创建选项配置
            option = typeof option === 'string' ? (optionCache[option] || createOption(option)) : {}

            // 创建函数队列 用来存放待执行的函数
            var list = [],
                isFirst = true,
                start, starts, memory;

            function fire(data) {

                //  将 memory 缓存起来
                memory = option.memory && data
                    // 第一次执行过后 将 flag 转为false 实现 once 功能
                isFirst = false;
                //  start 代表循环的起始位置
                let index = start || 0
                start = 0

                for (; index < list.length; index++) {
                    // 将函数绑定到上下文
                    if (list[index].apply(data[0], data[1]) === false && option.stopOnFalse) {
                        return 'stop'
                    }
                }

            }
            var self = {
                // 将函数依次添加到函数队列
                add: function() {
                    var args = Array.prototype.slice.call(arguments);
                    starts = list.length
                    args.forEach(fn => {
                            // 若传入的参数为函数 则将参数依次推入函数队列
                            if (jQuery.isFunction(fn)) {
                                list.push(fn)
                            }
                        })
                        // 如果设定 memory 则 触发fire 之后 再调用add 函数时 回立即执行add 内部的参数函数

                    if (memory) {
                        start = starts
                        fire(memory)
                    }
                    return this

                },
                /**
                 * 用来绑定执行函数的上下文
                 * @param {*} context 
                 * @param {*} data 
                 */
                fireWith: function(context, data) {
                    // 当设定为仅仅执行一次时 ， 只在第一次执行
                    if (!option.once || isFirst) {
                        fire([context, data])
                    }



                },
                fire: function() {
                    self.fireWith(this, arguments)
                }
            }

            return self

        },

        deferred: function() {
            // 创建一个延时对象
            var deferred = {};
            // 状态配置的列表 ( 包括三个状态及其函数调用队列 成功 resolve 、 失败 reject 、 进行中 notify)
            var tuple = [
                ['resolve', 'done', jQuery.callbacks('once memory'), 'resolved'],
                ['reject', 'fail', jQuery.callbacks('once memory'), 'rejected'],
                ['notify', 'progress', jQuery.callbacks('memory')]
            ];
            //  异步状态
            var state = 'pending';
            // ? 
            var promise = {
                state: function(params) {
                    return state
                },
                then: function(params) {

                },
                promise: function(obj) {
                    return obj != null ? jQuery.extend(obj, promise) : promise
                }
            }

            // 创建状态函数调用队列

            tuple.forEach(function(item) {
                //  创建当前状态的callbacks 对象 （ list：{ add:function , fire:function , fireWith:function ）
                var cb = item[2];
                //  状态提示字符串
                var stateString = item[3];

                if (stateString) {
                    //  promise 对象内部添加加入回调函数队列的方法 promise:{ done:func , fail:func}
                    promise[item[1]] = cb.add

                }
                //  像deferred对象添加 报告状态信息的方法
                deferred[item[0]] = function() {
                        //  调用deferred.resolve() | deferred.reject() 触发状态队列的回调函数调用
                        deferred[item[0] + 'With'](this === deferred ? promise : this, arguments);
                        return this
                    }
                    // 给deferred对象添加触发 状态函数队列的调用方法
                deferred[item[0] + 'With'] = cb.fireWith


            })

            //  将 promise 的方法合并到 deferred 上
            promise.promise(deferred)




            //  将延时对象返回
            return deferred

        },
        when: function(defer) {
            console.log(defer.promise())
            return defer.promise()

        }


    })


    function createOption(options) {
        var option = optionCache[options] = {};
        options.split(/\s+/).forEach(item => {
            option[item] = true
        });
        return option
    }




    //  将jQuery挂载到window（全局变量）上
    root.jQuery = root.$ = jQuery
(function(root) {


    var optionCache = {};

    /**
     *  jQuery 特点 
     * 
     * 1. 无new 构建实例对象
     * 
     * 2. 共享原型
     * 
     * 3. extend 拓展
     * 
     */

    function jQuery() {
        /**
         *  形成死循环  
         */
        // return new jQuery()  
        return new jQuery.prototype.init()
    }
    /**
     *  重写jQuery 原型，  来实现 构建无new操作符实例 ， 将jQuery的方法放在原型上
     */
    jQuery.prototype = {
            /**
             *  构建实例对象
             */
            init: function() {

            },
            html: function() {

            },
            css: function() {

                }
                //  ...
        }
        /**
         *  简写原型 
         */
    jQuery.fn = jQuery.prototype
        /**
         *  实现原型 共享 原型
         */
    jQuery.fn.init.prototype = jQuery.fn;


    /**
     *  extend 
     * 
     * 1. 可以给任意多个对象合并
     * 
     * 2. 可以拓展jQuery本身
     * 
     * 3. 可以拓展jQuery实例方法
     */

    jQuery.extend = jQuery.fn.extend = function() {
        /**
         * 参数分为多种情况 
         * 
         * 1. 有多个参数时 
         *      1. 第一个参数为 boolean 且为true 时  则代表要进行深拷贝
         *      
         *      2. 否则第一个参数必为对象
         * 
         * 2. 只有一个参数时
         *      
         *      1. 代表着给jQuery本身 / jQuery 实例 添加拓展方法
         * 
         */
        var target = arguments[0];
        var length = arguments.length;
        var deep = false
        var i = 1;

        if (Object.prototype.toString.call(target).slice(8, -1) === 'Array') {
            deep = true;
            target = arguments[1]
            i++
        }

        if (typeof target != 'object') {
            target = {}
        }

        if (i === length) {
            target = this;
            i--
        }



        for (; i < length; i++) {
            var option = arguments[i];
            for (var name in option) {
                var copy = option[name];
                var src = target[name];
                var isArray = Object.prototype.toString.call(copy).slice(8, -1) === 'Array';
                var isObject = Object.prototype.toString.call(copy).slice(8, -1) === 'Object'
                var clone = {};
                if (deep && (isArray || isObject)) {
                    if (isArray) {
                        clone = Object.prototype.toString.call(src).slice(8, -1) === 'Array' ? src : [];
                    } else if (isObject) {
                        clone = Object.prototype.toString.call(src).slice(8, -1) === 'Object' ? src : {}
                    } else {
                        clone = src
                    }
                    target[name] = jQuery.extend(deep, clone, copy)
                } else if (copy !== undefined) {
                    target[name] = copy
                }
            }
        }

        return target


    }


    jQuery.extend({
        isPlainObject: function(data) {
            return Object.prototype.toString.call(data).slice(8, -1) === 'Object'
        },
        isFunction: function(fn) {
            return Object.prototype.toString.call(fn).slice(8, -1) === 'Function'
        },
        isArray: function(data) {
            return Object.prototype.toString.call(data).slice(8, -1) === 'Array'
        },
        isBoolean: function(data) {
            return Object.prototype.toString.call(data).slice(8, -1) === 'Boolean'
        },

        callbacks: function(option) {
            //  option 判断option 是否为字符串 若是字符串，先查看缓存内有没，有就用缓存 ,没有就创建选项配置
            option = typeof option === 'string' ? (optionCache[option] || createOption(option)) : {}

            // 创建函数队列 用来存放待执行的函数
            var list = [],
                isFirst = true,
                start, starts, memory;

            function fire(data) {

                //  将 memory 缓存起来
                memory = option.memory && data
                    // 第一次执行过后 将 flag 转为false 实现 once 功能
                isFirst = false;
                //  start 代表循环的起始位置
                let index = start || 0
                start = 0

                for (; index < list.length; index++) {
                    // 将函数绑定到上下文
                    if (list[index].apply(data[0], data[1]) === false && option.stopOnFalse) {
                        return 'stop'
                    }
                }

            }
            var self = {
                // 将函数依次添加到函数队列
                add: function() {
                    var args = Array.prototype.slice.call(arguments);
                    starts = list.length
                    args.forEach(fn => {
                            // 若传入的参数为函数 则将参数依次推入函数队列
                            if (jQuery.isFunction(fn)) {
                                list.push(fn)
                            }
                        })
                        // 如果设定 memory 则 触发fire 之后 再调用add 函数时 回立即执行add 内部的参数函数

                    if (memory) {
                        start = starts
                        fire(memory)
                    }
                    return this

                },
                /**
                 * 用来绑定执行函数的上下文
                 * @param {*} context 
                 * @param {*} data 
                 */
                fireWith: function(context, data) {
                    // 当设定为仅仅执行一次时 ， 只在第一次执行
                    if (!option.once || isFirst) {
                        fire([context, data])
                    }



                },
                fire: function() {
                    self.fireWith(this, arguments)
                }
            }

            return self

        },

        deferred: function() {
            // create delayed object
            var deferred = {};
            // state (  成功 resolve 、  reject 、  notify)
            var tuple = [
                ['resolve', 'done', jQuery.callbacks('once memory'), 'resolved'],
                ['reject', 'fail', jQuery.callbacks('once memory'), 'rejected'],
                ['notify', 'progress', jQuery.callbacks('memory')]
            ];
            //  异步状态
            var state = 'pending';
            // ? 
            var promise = {
                state: function(params) {
                    return state
                },
                then: function(params) {

                },
                promise: function(obj) {
                    return obj != null ? jQuery.extend(obj, promise) : promise
                }
            }

            // 创建状态函数调用队列

            tuple.forEach(function(item) {
                //  创建当前状态的callbacks 对象 （ list：{ add:function , fire:function , fireWith:function ）
                var cb = item[2];
                //  状态提示字符串
                var stateString = item[3];

                if (stateString) {
                    //  promise 对象内部添加加入回调函数队列的方法 promise:{ done:func , fail:func}
                    promise[item[1]] = cb.add

                }
                //  像deferred对象添加 报告状态信息的方法
                deferred[item[0]] = function() {
                        //  调用deferred.resolve() | deferred.reject() 触发状态队列的回调函数调用
                        deferred[item[0] + 'With'](this === deferred ? promise : this, arguments);
                        return this
                    }
                    // 给deferred对象添加触发 状态函数队列的调用方法
                deferred[item[0] + 'With'] = cb.fireWith


            })

            //  将 promise 的方法合并到 deferred 上
            promise.promise(deferred)




            //  将延时对象返回
            return deferred

        },
        when: function(defer) {
            return defer.promise()

        }


    })


    function createOption(options) {
        var option = optionCache[options] = {};
        options.split(/\s+/).forEach(item => {
            option[item] = true
        });
        return option
    }




    //  将jQuery挂载到window（全局变量）上
    root.jQuery = root.$ = jQuery
})(this)